﻿
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Transactions;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Utils;

namespace TrackSystem.IO.Formula
{
    /// <summary>
    /// 实用运算工具
    /// </summary>
    public class FormulaTools
    {
        /// <summary>
        /// 应用句柄
        /// </summary>
        public static FormulaTools Instance;

        private ConcurrentDictionary<Guid, FmlBase> Items;

        /// <summary>
        /// 构造函数
        /// </summary>
        static FormulaTools()
        {
            Instance = new FormulaTools();
            Instance.CreateInstance();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        private void CreateInstance()
        {
            Items = new();
            
            // 添加所定义的函数
            KeywordsHelper.Instance.Insert("ramp", OperatorType.RAMP, 2, (num, id) => Step(num, id, OperatorType.RAMP));
            KeywordsHelper.Instance.Insert("angle", OperatorType.ANGLE, 2, (num, id) => Step(num, id, OperatorType.RAMP));
            KeywordsHelper.Instance.Insert("filter", OperatorType.FILTER, 2, (num, id) => Step(num, id, OperatorType.FILTER));
            KeywordsHelper.Instance.Insert("speed", OperatorType.SPEED, 1, (num, id) => Step(num, id, OperatorType.SPEED));
        }

        /// <summary>
        /// 求值
        /// </summary>
        /// <param name="op"></param>
        /// <returns></returns>
        public bool TryAdd(Operator op)
        {
            return op.Type switch
            {
                OperatorType.RAMP => Items.TryAdd(op.Id, new FmlRamp()),
                OperatorType.ANGLE => Items.TryAdd(op.Id, new AngleClac()),
                OperatorType.FILTER => Items.TryAdd(op.Id, new FilterClac()),
                OperatorType.SPEED => Items.TryAdd(op.Id, new SpeedClac()),
                _ => false
            };
        }

        /// <summary>
        /// 周期数据计算函数
        /// </summary>
        /// <param name="ops"></param>
        /// <param name="id"></param>
        /// <param name="op"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="NotImplementedException"></exception>
        public OperandDef Step(OperandDef[] ops, Guid id, OperatorType op)
        {
            if ( ops[0].DataType == DataTypeEnum.BOOLEAN || ops[0].DataType == DataTypeEnum.STRING)
                throw new ArgumentException($"{op}: 操作类型只能是整数或者浮点数");
            
            if ( op == OperatorType.SPEED)
            {
                if (Items.TryGetValue(id, out FmlBase fmlBase))
                {
                    return fmlBase.Step(ops[0]);
                }
            }
            
            if (op is OperatorType.RAMP or OperatorType.ANGLE or OperatorType.FILTER)
            {
                if (ops[1].DataType == DataTypeEnum.BOOLEAN || ops[1].DataType == DataTypeEnum.STRING)
                    throw new ArgumentException($"{op}: 操作类型只能是整数或者浮点数");

                if (Items.TryGetValue(id, out FmlBase fmlBase))
                {
                    return fmlBase.Step(ops[1], ops[0]);
                }
            }

            throw new NotImplementedException($"{op}: 特殊功能未实现");  
        }

        /// <summary>
        /// 函数求值
        /// </summary>
        /// <param name="id"></param>
        /// <param name="param1"></param>
        /// <param name="param2"></param>
        /// <returns></returns>
        public OperandDef Step(Guid id, OperandDef param1, OperandDef param2)
        {
            if (Items.TryGetValue(id, out FmlBase fmlBase))
            {
                return fmlBase.Step(param1, param2);
            }

            return new OperandDef(param1);
        }

        /// <summary>
        /// 函数求值
        /// </summary>
        /// <param name="id"></param>
        /// <param name="param1"></param>
        /// <returns></returns>
        public OperandDef Step(Guid id, OperandDef param1)
        {
            if (Items.TryGetValue(id, out FmlBase fmlBase))
            {
                return fmlBase.Step(param1);
            }

            return new OperandDef(param1);
        }


        /// <summary>
        /// 内存清除函数
        /// </summary>
        public void Clear()
        {
            Items.Clear();
        }
    };


    /// <summary>
    /// 公式运算的基类
    /// </summary>
    public class FmlBase
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public FmlBase()
        {
        }

        /// <summary>
        /// 运算函数
        /// </summary>
        /// <param name="param1">参数1</param>
        /// <param name="param2">参数2</param>
        /// <returns></returns>
        public virtual OperandDef Step(OperandDef param1, OperandDef param2)
        {
            return new OperandDef(param1.DataType);
        }

        /// <summary>
        /// 运算函数
        /// </summary>
        /// <param name="param1">参数1</param>
        /// <param name="param2">参数2</param>
        /// <returns></returns>
        public virtual OperandDef Step(OperandDef param1)
        {
            return new OperandDef(param1.DataType);
        }
    }

    /// <summary>
    /// 斜坡函数
    /// </summary>
    public class FmlRamp : FmlBase
    {
        private double Value;
        private bool bFirst;

        /// <summary>
        /// 构造函数
        /// </summary>
        public FmlRamp()
        {
            Value = 0;
            bFirst = true;
        }

        /// <summary>
        /// 斜坡函数，一秒内变化到给定的值
        /// </summary>
        /// <param name="param1">参数1</param>
        /// <param name="param2">参数2</param>
        /// <returns></returns>
        public override OperandDef Step(OperandDef param1, OperandDef param2)
        {
            double ratio = 0;
            
            // 第二个参数为零，这直接跟随输入参数1
            if (param2.DataType == DataTypeEnum.DOUBLE)
            {
                if (Math.Abs(param2.dVal) < double.Epsilon)
                {
                 Value = param1.dVal;
                 return param1;
                } 
                ratio = param2.dVal / 10.0;   
            }

            if (param2.DataType == DataTypeEnum.INTEGER)
            {
                if (param2.iVal == 0)
                {
                    Value = param1.iVal;
                    return param1;
                }
                ratio = param2.iVal / 10.0;
            }  
            
            // 首次启动
            if (bFirst)
            {
                if (param1.DataType == DataTypeEnum.INTEGER)
                    Value = param1.iVal;
                if (param1.DataType == DataTypeEnum.DOUBLE)
                    Value = param1.dVal;

                bFirst = false;
                return param1;
            }

            double current = 0;
            if (param1.DataType == DataTypeEnum.INTEGER)
            {
                current = param1.iVal;
            }
            if (param1.DataType == DataTypeEnum.DOUBLE)
            {
                current = param1.dVal;
            }

            if (Math.Abs(current - Value) < ratio)
                Value = current;
            else if (Value < current)
                Value += ratio;
            else
                Value -= ratio;
            
            return new OperandDef(Value);
        }
    }

    /// <summary>
    /// 角度计算
    /// </summary>
    public class AngleClac : FmlBase
    {
        private double Circle;      // 当前总圈数
        private double lastAngle;   // 上一次的角度值
        private bool bFirst;

        /// <summary>
        /// 构造函数
        /// </summary>
        public AngleClac()
        {
            Circle = 0;
            bFirst = true;
            lastAngle = 0;
        }

        /// <summary>
        /// 运算函数
        /// </summary>
        /// <param name="param1">当前的角度值</param>
        /// <param name="param2">每一圈的角度数据，默认是360度</param>
        /// <returns></returns>
        public override OperandDef Step(OperandDef param1, OperandDef param2)
        {
            // 首次启动
            double current = param1.DataType == DataTypeEnum.INTEGER ? param1.iVal : param1.dVal;

            if (bFirst)
            {
                lastAngle = current;
                Circle = 0;
                bFirst = false;  
                return param1;
            }

            double perimeter = param2.DataType == DataTypeEnum.INTEGER ? param2.iVal : param2.dVal;

            // 运行
            if (lastAngle > 3*perimeter/4 && current  < perimeter/4)
            {
                Circle += 1;
            }
            else if (lastAngle < perimeter / 4 && current > 3*perimeter /4)
            {
                Circle -= 1;
            }

            lastAngle = current;
            perimeter = Circle * perimeter + current;
            return new OperandDef(perimeter);
        }
    }

    /// <summary>
    /// 滤波函数
    /// </summary>
    public class FilterClac : FmlBase
    {
        private ConcurrentQueue<double>? Buffer; 
        private double Sum;           // 当前数据总和
        private bool bFirst;

        /// <summary>
        /// 构造函数
        /// </summary>
        public FilterClac()
        {
            Sum = 0;
            bFirst = true;
        }

        /// <summary>
        /// 运算函数
        /// </summary>
        /// <param name="param1">当前的角度值</param>
        /// <param name="param2">每一圈的角度数据，默认是360度</param>
        /// <returns></returns>
        public override OperandDef Step(OperandDef param1, OperandDef param2)
        {
            // 首次启动
            if (bFirst)
            {
                Buffer = new ();
                Sum = 0;
                bFirst = false;
                return param1.Clone();
            }

            double current = param1.DataType == DataTypeEnum.INTEGER ? param1.iVal : param1.dVal;
            int buffer_len = param2.DataType == DataTypeEnum.INTEGER ? param2.iVal : (int)param2.dVal;

            if (buffer_len <= 1) buffer_len = 1;
            while (Buffer.Count >= buffer_len)
            {
                if (Buffer.TryDequeue(out double d))
                {
                    Sum -= d;
                }
            }

            Buffer.Enqueue(current);
            Sum += current;

            // 运行
            return new OperandDef(Sum / Buffer.Count);
        }
    }

    /// <summary>
    /// 滤波函数
    /// </summary>
    public class SpeedClac : FmlBase
    {
        private double LastResult;    // 上次的计算结果
        private bool bFirst;

        /// <summary>
        /// 构造函数
        /// </summary>
        public SpeedClac()
        {
            bFirst = true;
        }

        /// <summary>
        /// 运算函数
        /// </summary>
        /// <param name="param1">当前的角度值</param>
        /// <param name="param2">每一圈的角度数据，默认是360度</param>
        /// <returns></returns>
        public override OperandDef Step(OperandDef param1)
        {
            // 首次启动
            if (bFirst)
            {
                LastResult = 0;
                bFirst = false;
                return param1.Clone();
            }

            double current = param1.DataType == DataTypeEnum.INTEGER ? param1.iVal : param1.dVal;
            double speed = (current - LastResult) / 0.1;
            LastResult = current;
            return new OperandDef(speed);
        }
    }
}
